package org.clockworkmages.games.anno1186.scripting;

import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineManager;

import org.clockworkmages.games.anno1186.GameBeansContext;
import org.clockworkmages.games.anno1186.GameGuiService;
import org.clockworkmages.games.anno1186.Injected;
import org.clockworkmages.games.anno1186.gui.GameUiUtil;

public class ScriptingService {

	private ScriptEngine jsEngine;

	@Injected
	private PlayerCharacterScriptingWrapper playerCharacterScriptingWrapper;
	@Injected
	private EnemyScriptingWrapper enemyScriptingWrapper;
	@Injected
	private PlotScriptingWrapper plotScriptingWrapper;
	@Injected
	private SituationScriptingWrapper situationScriptingWrapper;
	@Injected
	private UtilScriptingWrapper utilScriptingWrapper;

	public ScriptingService() {
		ScriptEngineManager sem = new ScriptEngineManager();
		jsEngine = sem.getEngineByName("JavaScript");
	}

	public boolean conditionIsMet(String condition) {
		Map<String, Object> model = getDefaultModel();
		return conditionIsMet(condition, model);
	}

	public void execute(String script) {
		Map<String, Object> model = getDefaultModel();
		execute(script, model);
	}

	public Object eval(String condition) {
		Map<String, Object> model = getDefaultModel();
		return eval(condition, model);
	}

	public Map<String, Object> getDefaultModel() {
		return getDefaultModel(false);
	}

	/**
	 * @param forEnemy
	 *            <b>true</b> is the chaacter that we're executing the Effect or
	 *            checking the Condition for is an enemy. If so, we need to
	 *            reverse the assignement of model values for "me" and "enemy"
	 *            attributes.
	 * @return
	 */
	public Map<String, Object> getDefaultModel(boolean forEnemy) {
		Map<String, Object> model = new HashMap<String, Object>();
		model.put("me", forEnemy ? enemyScriptingWrapper
				: playerCharacterScriptingWrapper);
		model.put("enemy", forEnemy ? playerCharacterScriptingWrapper
				: enemyScriptingWrapper);
		model.put("plot", plotScriptingWrapper);
		model.put("situation", situationScriptingWrapper);
		model.put("util", utilScriptingWrapper);
		model.put("input", GameBeansContext.getBean(GameGuiService.class)
				.getOptionTextInput().getText());
		return model;
	}

	public void execute(String expression, Map<String, Object> model) {
		eval(expression, model);
	}

	public boolean conditionIsMet(String expression, Map<String, Object> model) {
		if (expression == null || expression.isEmpty()) {
			return true;
		}
		return (Boolean) eval(expression, model);
	}

	public Object eval(String expression, Map<String, Object> model) {
		for (Entry<String, Object> entry : model.entrySet()) {
			jsEngine.put(entry.getKey(), entry.getValue());
		}
		try {
			return jsEngine.eval(expression);
		} catch (Exception e) {
			GameUiUtil.logError("Invalid expression: " + expression, e);
			return false;
		}
	}

}
